<template>
  <div class="pro-table" v-bind="rootAttrs">
    <!--  搜索表单  -->
    <div class="pro-table-search" v-show="isQueryForm">
      <slot name="form">
        <QueryFilter
          class="table-query-form"
          v-model="queryParams"
          v-bind="formProps"
          :columns="queryColumns"
          @search="search"
        >
          <template #control="scope">
            <slot name="form-control" v-bind="scope" />
          </template>
          <template v-for="(_item, name) in formSlots" #[name]="slotData" :key="name">
            <slot :name="name" v-bind="slotData" />
          </template>
        </QueryFilter>
      </slot>
    </div>

    <div class="pro-table-body">
      <!-- 工具栏 -->
      <slot name="toolbar">
        <TableToolbar
          v-if="toolbar"
          v-model:size="size"
          v-model:stripe="stripe"
          v-model:isQueryForm="isQueryForm"
          :columns="props.columns"
          @refresh="flush"
        >
          <slot name="toolbarHeader"></slot>
        </TableToolbar>
      </slot>

      <div class="pro-table-wrapper">
        <!--   表格头部     -->
        <slot name="tableHeader" />

        <el-table
          :data="tableData"
          v-bind="filteredAttrs"
          v-loading="tableLoading"
          :size="size"
          :stripe="stripe"
          @select="handleSelectChange"
          @select-all="handleSelectAll"
          ref="tableRef"
        >
          <!--   表格列       -->
          <TableColumn :columns="props.columns">
            <template v-for="(_item, name) in columnSlots" #[name]="slotData" :key="name">
              <slot :name="name" v-bind="slotData" />
            </template>
          </TableColumn>

          <!-- 插入至表格最后一行之后的插槽 -->
          <template #append>
            <slot name="append"></slot>
          </template>

          <template #empty>
            <slot name="empty">
              <el-empty class="table-empty" description="暂无数据" />
            </slot>
          </template>
        </el-table>

        <!--   分页器     -->
        <slot
          name="pagination"
          :pagination="tablePagination"
          :handleSizeChange="handleSizeChange"
          :handleCurrentChange="handleCurrentChange"
        >
          <el-pagination
            v-if="isPagination"
            v-bind="pagerProps"
            v-on="pagerEmits"
            :current-page="tablePagination.currentPage"
            :page-size="tablePagination.pageSize"
            :total="tablePagination.total"
            class="table-pagination"
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
          />
        </slot>
      </div>
    </div>
  </div>
</template>
<script setup>
import { computed, ref, useAttrs, useSlots, useTemplateRef } from 'vue'
import TableColumn from './table-column/index.vue'
import { QueryFilter } from '@/components/form/index.js'
import TableToolbar from './table-toolbar/index.vue'
import { reactivePick } from '@vueuse/core'
import useTableSelection from '@/hooks/use-table-selection.js'
import useTable from './hooks/use-table.js'
import usePagination from './hooks/use-pagination.js'
import useForm from './hooks/use-form.js'
import { dataOmit } from '@/utils/util.js'

defineOptions({
  inheritAttrs: false,
  name: 'ProTable',
})

const props = defineProps({
  request: { type: Function, default: null }, // 获取数据的方法 (params?: {pageSize,currentPage}) => {data,success,msg,total}
  postData: { type: Function, default: null }, // 对通过 request 获取的数据进行处理
  params: { type: Object, default: () => {} }, // 拥有request查询的额外参数
  data: { type: Array, default: null }, // table的数据，推荐使用request来获取数据
  requestAuto: { type: Boolean, default: true }, // 是否自动请求数据
  columns: { type: Array, default: () => [] }, // 表格列
  defaultSize: { type: String, default: 'default' }, // 默认表格大小
  defaultStripe: { type: Boolean, default: false }, // 默认表格斑马纹
  toolbar: { type: Boolean, default: true }, // 是否显示工具栏
  loading: { type: Boolean, default: false }, // 表格loading
  pagination: {
    type: [Boolean, Object],
    default() {
      return {
        currentPage: 1,
        pageSize: 10,
        total: 0,
      }
    },
  }, //  分页器配置 如果为false不显示分页器
  paginationProps: {
    type: Object,
    default() {
      return {
        layout: 'total, sizes, prev, pager, next',
        'page-sizes': [10, 20, 30, 40],
      }
    },
  }, // 分页器属性
  paginationEmits: { type: Object, default: () => {} }, // 分页器事件
  onLoad: { type: Function, default: null }, // 数据加载完成后触发，会多次触发
  onLoadingChange: { type: Function, default: null }, // loading 被修改时触发
  onRequestError: { type: Function, default: null }, // 数据加载失败时触发
  formProps: { type: Object, default: () => {} }, // 搜索表单属性
  formOtherColumns: { type: Array, default: () => [] }, // 表单额外的列
})

const emits = defineEmits(['flush', 'selectedChange'])
const slots = useSlots()
const attrs = useAttrs()

const size = ref(props.defaultSize || 'default') // 表格大小
const stripe = ref(props.defaultStripe || false) // 表格斑马纹
const tableRef = useTemplateRef('tableRef') // 表格ref

const filteredAttrs = dataOmit(['class', 'style', 'id'], attrs)
const rootAttrs = reactivePick(attrs, ['class', 'style', 'id'])

// =========================== 表单相关 =======================
const { queryParams, queryColumns, querySlotKeys, isQueryFilter, formSlots } = useForm(
  props.columns,
  slots,
  props.formOtherColumns,
)

// 如果没有表单插槽并且也没有查询过滤表单（不展示表单）那就置为null
const isQueryForm = ref(!slots.form && !isQueryFilter.value ? null : true) // 是否显示查询表单

// 实际的查询参数 合并查询参数和用户自定义参数
const paramsData = computed(() => {
  return {
    ...props.params,
    ...queryParams.value,
  }
})

// =========================== 表格查询相关 =======================

// 是否使用自定义数据
const hasCustomData = computed(() => {
  return props.data !== null && props.data !== undefined
})

// 是否显示分页器
const isPagination = computed(() => {
  return props.pagination != null && props.pagination !== false
})

const {
  tableData: requestTableData,
  pagination: requestPagination,
  loading: requestLoading,
  fetchDataList,
  handleSizeChange: requestHandleSizeChange,
  handleCurrentChange: requestHandleCurrentChange,
} = useTable(
  props.request,
  paramsData,
  props.postData,
  props.onLoad,
  props.onRequestError,
  props.onLoadingChange,
  isPagination.value,
  isPagination.value ? props.pagination : null,
)

// 自动请求数据
props.requestAuto && fetchDataList()

// 表格列插槽
const columnSlots = computed(() => {
  // 排除掉不需要的插槽（当前的组件其他的插槽）
  const excludedSlots = [
    'form',
    'toolbar',
    'tableHeader',
    'append',
    'empty',
    'pagination',
    'toolbarHeader',
    'form-control',
    ...querySlotKeys.value,
  ]
  return dataOmit(excludedSlots, slots)
})

// 表格数据
const tableData = computed(() => {
  // 使用自定义数据
  if (hasCustomData.value) {
    // if (isPagination.value) {
    //   const { currentPage, pageSize } = isPagination.value
    //   data.slice((currentPage - 1) * pageSize, currentPage * pageSize)
    // }
    return props.data
  } else {
    return requestTableData.value
  }
})

// 表格loading
const tableLoading = computed(() => {
  return props.loading || requestLoading.value
})

// 刷新
const flush = () => {
  fetchDataList()
  emits('flush')
}

const search = () => {
  fetchDataList()
}

// =========================== 选择项 =======================

// 表格选择项
const { handleSelectChange, handleSelectAll, multipleSelection, clearSelection } =
  useTableSelection(
    tableData,
    tableRef,
    (val) => {
      emits('selectedChange', val)
    },
    attrs['row-key'] || 'id',
  )

// =========================== 分页器 =======================

// 表格分页器
const tablePagination = computed(() => {
  if (hasCustomData.value) {
    return props.pagination
  } else {
    return requestPagination
  }
})

const { handleSizeChange, handleCurrentChange, pagerEmits, pagerProps } = usePagination(
  props,
  requestHandleSizeChange,
  requestHandleCurrentChange,
)

// =========================== 暴露 =======================

defineExpose({
  tableRef,
  queryParams,
  size,
  stripe,
  isQueryForm,
  tableData,
  fetchDataList,
  search,
  loading: tableLoading,
  pagination: tablePagination,
  handleSizeChange,
  handleCurrentChange,
  multipleSelection,
  clearSelection,
})
</script>

<style scoped lang="scss"></style>
